﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using ComponentFactory.Krypton.Toolkit;

using Microsoft.Xna.Framework;

using SceneEngine;

namespace SceneEditor
{
    public partial class Editor
    {
        /// <summary>
        /// Creates a duplicate set of the selected objects.
        /// </summary>
        private void tsDuplicateObjects_Click(object sender, EventArgs e)
        {
            if (gizmo.Transforms.Count > 0)
            {
                BaseObject[] targets = new BaseObject[gizmo.Transforms.Count];

                for (int i = 0; i < gizmo.Transforms.Count; i++)
                {
                    string selectedName = gizmo.Transforms.Objects[i];
                    BaseObject selectedObject = scene.GetBaseObject(selectedName);

                    string name = Path.GetFileName(selectedObject.ModelPath);
                    name = CheckName(name);

                    BaseObject entity = new BaseObject(name, modelManager.Models[selectedObject.ModelPath]);

                    entity.Position = selectedObject.Position;
                    entity.Rotation = selectedObject.Rotation;
                    entity.Scale = selectedObject.Scale;

                    entity.Vertices = selectedObject.Vertices;
                    entity.ModelPath = selectedObject.ModelPath;

                    entity.PlaceOnTerrain = selectedObject.PlaceOnTerrain;

                    scene.Add(name, entity);
                    targets[i] = entity;

                    AddObjectToTree(name, "Objects");

                    gizmo.Transforms.ChangeName(i, entity.Name);
                    SelectionChanged();
                }

                undoActions.Push(new AddObjectAction(targets), true);
            }
        }

        /// <summary>
        /// Selects the object that was clicked in the tree.
        /// </summary>
        private void tvObjects_SelectObject(object sender, MouseEventArgs e)
        {
            HandleNodeLeftClick();

            if (e.Button == MouseButtons.Right && editor.tvObjects.SelectedNodes.Count == 1)
            {
                bool isChildOfObject = IsNodeChildOfObjects(editor.tvObjects.SelectedNode);

                if (isChildOfObject)
                {
                    editor.AddObjectGroupMenu.Tag = editor.tvObjects.SelectedNode;
                    editor.AddObjectGroupMenu.Show(editor.tvObjects, e.Location);
                }
            }
        }

        private bool IsNodeChildOfObjects(TreeNode node)
        {
            bool isChildOfObject = node.Text == "Objects";
            TreeNode parentNode = node.Parent;

            while (parentNode != null && isChildOfObject == false)
            {
                if (parentNode.Text == "Objects")
                {
                    isChildOfObject = true;
                }

                parentNode = parentNode.Parent;
            }
            return isChildOfObject;
        }

        protected void HandleNodeLeftClick()
        {
            gizmo.Transforms.Clear();

            if (editor.tvObjects.SelectedNodes.Count > 0)
            {
                if (IsNodeChildOfObjects(editor.tvObjects.SelectedNode) == false)
                {
                    return;
                }

                if (editor.tvObjects.SelectedNode.ImageIndex == 0)
                {
                    return;
                }

                foreach (TreeNode node in editor.tvObjects.SelectedNodes)
                {
                    gizmo.Transforms.Add(node.Text);
                }

                BaseObject currentBaseObject = scene.GetBaseObject(editor.tvObjects.SelectedNode.Text);

                if (gizmo.Transforms.Count == 1)
                {
                    editor.propertyGrid.SelectedObject = currentBaseObject;
                }
                else
                {
                    BaseObject[] selectedBaseObjects = new BaseObject[gizmo.Transforms.Count];

                    for (int i = 0; i < selectedBaseObjects.Length; i++)
                    {
                        selectedBaseObjects[i] = scene.GetBaseObject(gizmo.Transforms.Objects[i]);
                    }

                    editor.propertyGrid.SelectedObjects = selectedBaseObjects;
                }
            }
        }
        /// <summary>
        /// Selects every object in the scene.
        /// </summary>
        private void tsSelectAll_Click(object sender, EventArgs e)
        {
            gizmo.Transforms.Clear();

            foreach (string name in scene.ObjectNames)
            {
                gizmo.Transforms.Add(name);
            }

            SelectionChanged();
        }

        /// <summary>
        /// Adds a new object to the scene.
        /// </summary>
        private void tvAssets_AddObject(object sender, MouseEventArgs e)
        {
            TreeNode selectedItem = editor.tvAssets.SelectedNode;

            if (selectedItem == null)
            {
                return;
            }

            if (selectedItem.Tag != null)
            {
                if (editor.cbPlacement.Text == "Clicked")
                {
                    placingObjects = true;
                    editor.Viewport.Focus();

                    return;
                }

                string entityName = ChooseObjectName("", true);

                if (entityName == null)
                {
                    return;
                }

                BaseObject entity = new BaseObject(entityName, modelManager.Models[selectedItem.Tag as string]);
                entity.ModelPath = selectedItem.Tag as string;

                BoundingSphere nodeSphere = BoundingSphere.CreateFromBoundingBox(entity.Bounds);

                if (editor.cbPlacement.Text == "Origin")
                {
                    entity.Position = Vector3.Zero;
                }

                else if (editor.cbPlacement.Text == "Camera")
                {
                    entity.Position = camera.Position + (camera.Forward * nodeSphere.Radius);

                    if (editor.btnSnapToGrid.Checked)
                    {
                        entity.Position = new Vector3((int)entity.Position.X / (int)gridSpacing,
                            (int)entity.Position.Y / (int)gridSpacing, (int)entity.Position.Z / (int)gridSpacing);

                        entity.Position *= gridSpacing;
                    }
                }

                entity.SetUpBounds();

                scene.Add(entityName, entity);
                undoActions.Push(new AddObjectAction(entity), true);

                AddObjectToTree(entityName, "Objects");

                gizmo.Transforms.Clear();
                gizmo.Transforms.Add(entity.Name);

                editor.Viewport.Focus();
                SelectionChanged();
            }
        }
        /// <summary>
        /// Removes the currently selected objects from the scene.
        /// </summary>
        private void btnRemoveObject_Click(object sender, EventArgs e)
        {
            if (editor.tvObjects.SelectedNodes.Count == 0)
            {
                return;
            }

            List<BaseObject> selectedObjects = new List<BaseObject>();

            string rootToDelete = string.Empty;

            if (editor.tvObjects.SelectedNode.ImageIndex == 0)
            {
                rootToDelete = editor.tvObjects.GetGroupPathFromNode(editor.tvObjects.SelectedNode, true);
            }

            DeleteObjectNodes(editor.tvObjects.SelectedNodes, selectedObjects);
            undoActions.Push(new RemoveObjectAction(selectedObjects.ToArray(), rootToDelete), true);

            editor.tvObjects.ClearSelectedNodes();
            gizmo.Transforms.Clear();
        }

        private void DeleteObjectNodes(List<TreeNode> list, List<BaseObject> selectedObjects)
        {
            for (int i = list.Count - 1; i >= 0; i--)
            {
                TreeNode node = list[i];

                if (node.Nodes.Count > 0)
                {
                    DeleteObjectNodes(node.Nodes, selectedObjects);
                }
                else if (node.ImageIndex == 1)
                {
                    selectedObjects.Add(scene.GetBaseObject(node.Text));
                    scene.Remove(node.Text);
                }

                node.Remove();
            }
        }
        private void DeleteObjectNodes(TreeNodeCollection list, List<BaseObject> selectedObjects)
        {
            for (int i = list.Count - 1; i >= 0; i--)
            {
                TreeNode node = list[i];

                if (node.Nodes.Count > 0)
                {
                    DeleteObjectNodes(node.Nodes, selectedObjects);
                }
                else if (node.ImageIndex == 1)
                {
                    selectedObjects.Add(scene.GetBaseObject(node.Text));
                    scene.Remove(node.Text);
                }

                node.Remove();
            }
        }

        /// <summary>
        /// Updates the object tree with the name change.
        /// </summary>
        private void scene_ObjectRenamed(object sender, SceneEngine.RenamedEventArgs e)
        {
            int index = gizmo.Transforms.Objects.IndexOf(e.PreviousName);

            if (index < 0)
            {
                return;
            }

            gizmo.Transforms.Objects[index] = e.NewName;

            TreeNode node = editor.tvObjects.GetNodeFromGroupPath(scene.GetBaseObject(e.NewName).GroupPath, e.PreviousName);
            node.Name = node.Text = e.NewName;
        }
    }
}
